#include "speech_widget.h"
#include "ui_widget.h"

SpeechWidget::SpeechWidget(QString resPath,QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SpeechWidget)
{
    ui->setupUi(this);
#ifdef Q_OS_ANDROID
    ui->horizontalSlider->setValue(10);
#else
    ui->horizontalSlider->setValue(80);
#endif
    qDebug()<<manager->supportedSchemes();
    connect(manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(requestFinished(QNetworkReply*)));
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleRate(16000);
    format.setSampleSize(16);
    format.setSampleType(QAudioFormat::SignedInt);
    format.setChannelCount(1);
    input = new QAudioInput(format,this);
    output = new QAudioOutput(format,this);
    tts = new QTextToSpeech(this);
    this->resPath = resPath;
    GetToken();
    connect(output,SIGNAL(stateChanged(QAudio::State)),this,SLOT(audioState(QAudio::State)));
    connect(tts,&QTextToSpeech::stateChanged,this,&SpeechWidget::ttsState);
}

SpeechWidget::~SpeechWidget()
{
    delete ui;
}

QString SpeechWidget::GetSubString(QString src, QString pattern)
{
    return GetSubStringEX(src,pattern)[0];
}

QStringList SpeechWidget::GetSubStringEX(QString src, QString pattern)
{
    QStringList ret;
    QRegExp rx(pattern);
    rx.setMinimal(true);
    int pos = 0;
    while((pos = rx.indexIn(src,pos)) != -1){
        ret<<rx.capturedTexts()[1];
        pos+=rx.matchedLength();
    }
    return ret;
}

QString SpeechWidget::Unescape(QString src)
{
    QTextDocument doc;
    doc.setHtml(src);
    return doc.toPlainText();
}

void SpeechWidget::SendRequest(QString url, QString data,const QMap<QString,QString> * header)
{
    QNetworkRequest req((QUrl(url)));
    if(header != nullptr){
        for(auto i=header->begin();i!=header->end();++i){
            req.setRawHeader(i.key().toLatin1(),i.value().toLatin1());
        }
    }
    if(data!=""){
        req.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("application/json"));
        manager->post(req,data.toUtf8());
    }else{
        manager->get(req);
    }
}

void SpeechWidget::STT(QByteArray data)
{
    QJsonObject param;
    param["speech"] = QString(data.toBase64());
    param["format"] = "pcm";
    param["rate"] = 16000;
    param["channel"] = 1;
    param["token"] = token;
    //param["lan"] = "zh";
    param["cuid"] = baidu_apikey;
    param["len"] = data.size();
    SendRequest("http://vop.baidu.com/server_api",QJsonDocument(param).toJson());
}

void SpeechWidget::GetToken()
{
    SendRequest(QString("http://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2&").arg(baidu_apikey,baidu_seckey));
}

void SpeechWidget::AIUI(QString msg)
{
    if(msg=="")return;
    QString timestr = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
    QJsonObject param;
    param["scene"] = "main_box";
    param["auth_id"] = aiui_auth;
    param["data_type"] = "text";
    QByteArray param_str_base = QJsonDocument(param).toJson().toBase64();
    QMap<QString,QString> header;
    header["X-Param"] = param_str_base;
    header["X-CurTime"] = timestr;
    QString tmp = aiui_apikey+timestr+QString(param_str_base);
    header["X-CheckSum"] = MD5(tmp.toLatin1());
    header["X-Appid"] = aiui_appid;
    SendRequest("http://openapi.xfyun.cn/v2/aiui",msg,&header);
}

void SpeechWidget::TTS(QString msg)
{
    if(msg=="")return;
    if(ui->radioButton->isChecked()){
        tts->stop();
        tts->say(msg);
    }else{
        QString url = QString("http://tsn.baidu.com/text2audio?tex=%1&lan=zh&cuid=%2&ctp=1&tok=%3&per=0&aue=4").arg(QUrl::toPercentEncoding(msg),baidu_appid,token);
        SendRequest(url);
    }
}

void SpeechWidget::WriteFile(QString file, QByteArray data)
{
    QFile f(file);
    f.open(QIODevice::WriteOnly);
    f.write(data);
    f.close();
}

QString SpeechWidget::MD5(QByteArray data)
{
    return QCryptographicHash::hash(data,QCryptographicHash::Md5).toHex();
}

void SpeechWidget::requestFinished(QNetworkReply *reply)
{
    qDebug()<<reply->url();

    QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
    if(statusCode.isValid())
        qDebug() << "status code=" << statusCode.toInt();

    QVariant reason = reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
    if(reason.isValid())
        qDebug() << "reason=" << reason.toString();

    QNetworkReply::NetworkError err = reply->error();
    if(err != QNetworkReply::NoError)
        qDebug() << "Failed: " << reply->errorString();
    else {
        if(reply->url().url().indexOf("openapi.baidu.com/oauth/2.0/token") != -1){
            QJsonObject ret = QJsonDocument::fromJson(reply->readAll()).object();
            token = ret["access_token"].toString();
            ui->label->setText("语音模块初始化完成");
        }else if(reply->url().url().indexOf("vop.baidu.com/server_api") != -1){
            QJsonObject ret = QJsonDocument::fromJson(reply->readAll()).object();
            QString msg = ret["result"].toArray()[0].toString();
            emit ReceiveSTT(msg);
            ui->label->setText(msg.left(40));
            AIUI(msg);
        }else if(reply->url().url().indexOf("openapi.xfyun.cn/v2/aiui") != -1){
            QJsonObject ret = QJsonDocument::fromJson(reply->readAll()).object();
            QString send = ret["data"].toArray()[0].toObject()["intent"].toObject()["answer"].toObject()["text"].toString();
            TTS(send);
            emit ReceiveReply(ret);
        }else if(reply->url().url().indexOf("tsn.baidu.com/text2audio") != -1){
            WriteFile("./baidu.pcm",reply->readAll());
            tmpfile.close();
            tmpfile.setFileName("./baidu.pcm");
            tmpfile.open(QIODevice::ReadOnly);
            output->stop();
            output->start(&tmpfile);
        }else{
            qDebug()<<QString(reply->readAll());
        }
    }
}

void SpeechWidget::on_pushButton_clicked()
{
    if(input->state() == QAudio::State::ActiveState){
        input->stop();
        sound.setSource(QUrl::fromLocalFile(resPath+"sound/dong.wav"));
        sound.setVolume(0.8);
        sound.play();
        STT(buff->data());
        buff->deleteLater();
        ui->pushButton->setText("开始录音");
        emit ButtonState(true);
    }else{
        passtime = QDateTime::currentDateTimeUtc().toTime_t();
        isfirst = true;
        buff = new QBuffer;
        buff->open(QIODevice::ReadWrite);
        connect(buff,SIGNAL(readyRead()),this,SLOT(readData()));
        sound.setSource(QUrl::fromLocalFile(resPath+"sound/ding.wav"));
        sound.setVolume(0.8);
        sound.play();
        input->start(buff);
        ui->pushButton->setText("结束录音");
        emit ButtonState(false);
    }
}

void SpeechWidget::on_pushButton_3_clicked()
{
    TTS(ui->plainTextEdit->toPlainText());
}

void SpeechWidget::on_pushButton_4_clicked()
{
    AIUI(ui->plainTextEdit_2->toPlainText());
}

void SpeechWidget::readData()
{
    if(buff->size()<200)return;
    double aver = 0.0;
    const char * ptr = buff->buffer().data() + buff->size();
    for(int i=0;i<100;++i){ptr-=2;aver += qAbs(qFromLittleEndian<qint16>(ptr));}
    short value = ((double)aver / 100.0 / 32767.0) * 100;
    ui->progressBar->setValue(value);

    if(value >= ui->horizontalSlider->value()){
        isfirst = false;
        passtime = QDateTime::currentDateTimeUtc().toTime_t();
        qDebug()<<value;
    }
    if(ui->checkBox->isChecked()){
        if(isfirst){
            if(QDateTime::currentDateTimeUtc().toTime_t() - passtime >= first_wait_time) emit on_pushButton_clicked();
        }else{
            if(QDateTime::currentDateTimeUtc().toTime_t() - passtime >= max_wait_time) emit on_pushButton_clicked();
        }
        //auto stop
    }
}

void SpeechWidget::audioState(QAudio::State state)
{
    qDebug()<<state;
    if(state == QAudio::State::IdleState){
        //when it stopped
        emit ChangeState(false);
    }else if(state == QAudio::State::ActiveState){
        //when it speak
        emit ChangeState(true);
    }
    if(state==QAudio::State::IdleState && ui->checkBox->isChecked()){
        emit on_pushButton_clicked();
    }
}

void SpeechWidget::ttsState(QTextToSpeech::State state)
{
    qDebug()<<state;
    if(state == QTextToSpeech::State::Ready){
        //when it stopped
        emit ChangeState(false);
    }else if(state == QTextToSpeech::State::Speaking){
        //when it speak
        emit ChangeState(true);
    }
    if(state==QTextToSpeech::State::Ready && ui->checkBox->isChecked()){
        emit on_pushButton_clicked();
    }
}

